package com.xxwu.springgobang.component;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxwu.springgobang.model.Room;
import com.xxwu.springgobang.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

@Component
@Slf4j
public class Matcher {

    @Resource
    private OnlineUserManager onlineUserManager;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private RoomManager roomManager;

    private Queue<User> lowQueue = new LinkedList<>();
    private Queue<User> normalQueue = new LinkedList<>();
    private Queue<User> highQueue = new LinkedList<>();
    private Queue<User> veryHighQueue = new LinkedList<>();

    //用户入匹配队列
    public void add(User user) {
        if(user.getScore() < 2000){
            synchronized (lowQueue) {
                lowQueue.add(user);
                lowQueue.notify();
            }
            log.info("玩家：{}放到了lowQueue中", user.getUsername());
        } else if(user.getScore() >= 2000 && user.getScore() < 4000){
            synchronized (normalQueue) {
                normalQueue.add(user);
                normalQueue.notify();
            }
            log.info("玩家：{}放到了normalQueue中", user.getUsername());
        } else if(user.getScore() >= 4000 && user.getScore() < 6000){
            synchronized (highQueue) {
                highQueue.add(user);
                highQueue.notify();
            }
            log.info("玩家：{}放到了highQueue中", user.getUsername());
        } else if(user.getScore() >= 6000){
            synchronized (veryHighQueue) {
                veryHighQueue.add(user);
                veryHighQueue.notify();
            }
            log.info("玩家：{}放到了veryHighQueue中", user.getUsername());
        }
    }

    public void remove(User user) {
        if(user.getScore() < 2000){
            synchronized (lowQueue) {
                lowQueue.remove(user);
            }
            log.info("玩家：{}从lowQueue中移除", user.getUsername());
        } else if(user.getScore() >= 2000 && user.getScore() < 4000){
            synchronized (normalQueue) {
                normalQueue.remove(user);
            }
            log.info("玩家：{}从normalQueue中移除", user.getUsername());
        } else if(user.getScore() >= 4000 && user.getScore() < 6000){
            synchronized (highQueue) {
                highQueue.remove(user);
            }
            log.info("玩家：{}从highQueue中移除", user.getUsername());
        } else if(user.getScore() >= 6000){
            synchronized (veryHighQueue) {
                veryHighQueue.remove(user);
            }
            log.info("玩家：{}从veryHighQueue中移除", user.getUsername());
        }
    }

    //实现四个扫描线程对匹配队列进行扫描
    //在扫描时还要注意线程安全问题
    //针对同一个队列操作时才涉及到线程安全的问题，对不同队列不涉及
    //四个线程都是针对这个方法进行操作，将该方法进行加锁即可
    //注意此处加锁，不是线程1获取到锁之后其他线程则无法进行后续扫描，这里是有四个不同的锁对象，这四个线程不冲突
    private Matcher(){
        Thread t1 = new Thread(()-> {
            while(true){
                handlerMatch(lowQueue);
            }
        });
        t1.start();

        Thread t2 = new Thread(()-> {
            while(true){
                handlerMatch(normalQueue);
            }
        });
        t2.start();

        Thread t3 = new Thread(()-> {
            while(true){
                handlerMatch(highQueue);
            }
        });
        t3.start();

        Thread t4 = new Thread(()-> {
            while(true){
                handlerMatch(veryHighQueue);
            }
        });
        t4.start();
    }



    private void handlerMatch(Queue<User> matchQueue) {
        synchronized (matchQueue) {

            try{
                //队列扫描逻辑
                //要进行多次判断，否则队列为空时加入一个元素还是不能继续向下执行，必须够两个才可以
                while(matchQueue.size() < 2){
//                    return;   在这里若使用return，线程返回后将再次进入下一次循环，一直占用cpu资源，线程进入忙等
//                    但使用sleep也不太优雅，若队列中只有玩家1时，进入if判定进行等待，但下一毫秒玩家2开始匹配
//                    此时玩家1就必须一直等待直到sleep时间到才行
                    matchQueue.wait();

                }
                //尝试获取两个玩家
                User player1 = matchQueue.poll();
                User player2 = matchQueue.poll();
                log.info("匹配到玩家1:{}  匹配到玩家2:{}", player1.getUsername(), player2.getUsername());

                //检查玩家在线状态，以防玩家在匹配中突然下线
                WebSocketSession session1 = onlineUserManager.getSessionFromGameHall(player1.getUserId());
                WebSocketSession session2 = onlineUserManager.getSessionFromGameHall(player2.getUserId());

                //玩家1下线，将玩家2放回匹配队列
                if(session1 == null){
                    System.out.println("1========");
                    matchQueue.offer(player2);
                    return;
                }
                //玩家2下线，将玩家1放回匹配队列
                if(session2 == null){
                    System.out.println("2========");
                    matchQueue.offer(player1);
                    return;
                }

                //若玩家是同一个人,理论上不会出现，但以防前面的程序在运行时出现bug在这里做一次判定
                if(session1 == session2){
                    System.out.println("3========");
                    matchQueue.offer(player1);
                    return;
                }

                //将玩家放进同一个游戏房间
                Room room = new Room();
                roomManager.add(room,player1.getUserId(), player2.getUserId());
                log.info("玩家1：{}与玩家2：{}进入房间：{}", player1.getUsername(), player2.getUsername(), room.getRoomId());

                //向玩家发送匹配成功信息
                MatchResponds responds1 = new MatchResponds();
                responds1.setMessage("matchSuccess");
                //将返回的信息转为JSON字符串格式
                session1.sendMessage(new TextMessage(objectMapper.writeValueAsString(responds1)));
                log.info("匹配成功的responds1：" ,responds1);
                MatchResponds responds2 = new MatchResponds();
                responds2.setMessage("matchSuccess");
                session2.sendMessage(new TextMessage(objectMapper.writeValueAsString(responds2)));
                log.info("匹配成功的responds2：" ,responds1);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }


    }
}
